home *** CD-ROM | disk | FTP | other *** search
- { Listing 3 -- floppy.c}
-
-
- ** This file contains the various functions used to
- ** control the Floppy Disk Controller (FDC).
- */
- #include <stdio.h>
- #include <dos.h>
- #include "floppy.h"
- #include "delay.h"
-
- /* Global data */
- byte Results[7];
-
- /* Command Strings */
- static byte SpecifyCmd[] = { FdcCmdSpecify, 0, 0 };
- static byte SenseDriveCmd[] = { FdcCmdSenseDrive, 0 };
- static byte SenseIntCmd[] = { FdcCmdSenseInt };
- static byte RecalCmd[] = { FdcCmdRecalibrate, 0 };
- static byte SeekCmd[] = { FdcCmdSeek, 0, 0 };
- static byte ReadCmd[] = { FdcCmdRead, 0, 0, 0, 0, 2, 9,
- 0x2a, 0xff };
- static byte ReadTrackCmd[] = { FdcCmdReadTrack, 0, 0, 0,
- 0, 2, 9, 0x2a, 0xff };
- static byte WriteCmd[] = { FdcCmdWrite, 0, 0, 0, 0, 2, 9,
- 0x2a, 0xff };
- static byte FormatTrackCmd[] = { FdcCmdFormatTrack, 0, 2,
- 9, 0x50, 0xff };
-
- /* Local data */
- static byte StepRateTime = 0x0c,
- HeadUnloadTime = 0x0f,
- HeadLoadTime = 0x02;
- static byte Drive;
- static byte Cylinder = 0, Head = 0;
- static unsigned BytesPerSector = 512;
- static byte SectorsPerTrack = 9;
- volatile static byte Interrupt = 0;
- static void interrupt (*oldFdcInt)(void);
-
- #define TRUE 1
- #define FALSE 0
-
- /* Process FDC Interrupt request */
- void interrupt fdcInterrupt(void)
- {
- Interrupt = TRUE;
- (*oldFdcInt)();
- }
-
- /* Set local drive parameters value */
- void fdcSetDriveParms(byte srt, byte hut, byte hlt)
- {
- StepRateTime = srt;
- HeadUnloadTime = hut;
- HeadLoadTime = hlt;
-
- SpecifyCmd[1] = (StepRateTime << 4) | HeadUnloadTime;
- SpecifyCmd[2] = HeadLoadTime << 1;
-
- /* Setup interrupt hook, if not already set */
- if (oldFdcInt == NULL)
- {
- oldFdcInt = getvect(0x0e);
- setvect(0x0e, fdcInterrupt);
- }
- }
-
- /* Reset FDC interupt vector */
- void fdcResetParms(void)
- {
- if (oldFdcInt != NULL)
- {
- setvect(0x0e, oldFdcInt);
- oldFdcInt = NULL;
- }
- }
-
- /* Reset FDC and drives */
- void fdcReset(void)
- {
- /*
- ** Turn on the FDC's reset mode, disable DMA
- ** transfers, and turn off all drive motors.
- */
- fdcSetDOR(FdcDorReset | FdcDorDmaDisable);
-
- fdcDelay();
-
- /* turn off reset mode and enable DMA transfers */
- fdcSetDOR(FdcDorClearReset | FdcDorDmaEnable);
-
- /* Get the drive status */
- fdcSenseDrive();
-
- /* Remind the controller what parameters we are using */
- fdcSpecify();
- }
-
- /* Select, and turn on, the specified drive */
- void fdcSelectDrive(byte drive)
- {
- drive &= 0x03; /* Limit drive numbers from 0 to 3 */
- Drive = drive;
-
- /* Set Command strings based on drive number */
- SenseDriveCmd[1] = (SenseDriveCmd[1] & ~0x03) | Drive;
- RecalCmd[1] = Drive;
-
- fdcSetDOR((FdcDorDriveOn << drive) | FdcDorDmaEnable |
- FdcDorClearReset | drive);
- }
-
- /* Specify drive parameters */
- int fdcSpecify(void)
- {
- fdcSendCmd(SpecifyCmd, sizeof(SpecifyCmd));
- return fdcResultPhase(FALSE, 0);
- }
-
- /* Sense Drive Status */
- unsigned fdcSenseDrive(void)
- {
- fdcSendCmd(SenseDriveCmd, sizeof(SenseDriveCmd));
- return fdcResultPhase(TRUE, 1);
- }
-
- /* Get information concerning the last drive interrupt */
- unsigned fdcSenseInterrupt(void)
- {
- fdcSendCmd(SenseIntCmd, sizeof(SenseIntCmd));
- return fdcResultPhase(TRUE, 2);
- }
-
- /* Recalibrate the drive */
- unsigned fdcRecalibrate(void)
- {
- Cylinder = Head = 0;
-
- fdcSendCmd(RecalCmd, sizeof(RecalCmd));
-
- fdcWaitForInt();
- return fdcSenseInterrupt();
- }
-
- /* Seek the drive heads to the specified cylinder */
- unsigned fdcSeek(int head, int cylinder)
- {
- Cylinder = cylinder;
- Head = head;
-
- SeekCmd[1] = (head << 2) | Drive;
- SeekCmd[2] = cylinder;
-
- fdcSendCmd(SeekCmd, sizeof(SeekCmd));
-
- fdcWaitForInt();
- return fdcSenseInterrupt();
- }
-
- /*
- ** Read a number of sectors from the current track into
- ** the read buffer passed by the caller.
- */
- unsigned fdcReadSectors(int sector, unsigned nsect,
- void* buffer)
- {
- unsigned byteCount = nsect * BytesPerSector;
-
- /* Test for buffer fitting within a single DMA page */
- if (!dmaPageTest(buffer, byteCount))
- return -5;
-
- /* Setup DMA Controller */
- dmaSetup(DmaCmdRead, byteCount, buffer);
-
- ReadCmd[1] = (Head << 2) | Drive;
- ReadCmd[2] = Cylinder;
- ReadCmd[3] = Head;
- ReadCmd[4] = sector;
-
- fdcSendCmd(ReadCmd, sizeof(ReadCmd));
- return fdcResultPhase(FALSE, 7);
- }
-
- unsigned fdcReadTrack(void* buffer)
- {
- /* Test for buffer fitting within DMA boundries */
- if (!dmaPageTest(buffer, SectorsPerTrack *
- BytesPerSector))
- return -5;
-
- /* Setup DMA Controller */
- if (!dmaSetup(DmaCmdRead, SectorsPerTrack *
- BytesPerSector, buffer))
- return -5;
-
- ReadTrackCmd[1] = (Head << 2) | Drive;
- ReadTrackCmd[2] = Cylinder;
- ReadTrackCmd[3] = Head;
-
- fdcSendCmd(ReadTrackCmd, sizeof(ReadTrackCmd));
- return fdcResultPhase(FALSE, 7);
- }
-
- /*
- ** Write a number of sectors from the current track from
- ** the read buffer passed by the caller.
- */
- unsigned fdcWriteSectors(int sector, unsigned nsect,
- void* buffer)
- {
- unsigned byteCount = nsect * BytesPerSector;
-
- /* Test for buffer fitting within a single DMA page */
- if (!dmaPageTest(buffer, byteCount))
- return -5;
-
- /* Setup DMA Controller */
- dmaSetup(DmaCmdWrite, byteCount, buffer);
-
- WriteCmd[1] = (Head << 2) | Drive;
- WriteCmd[2] = Cylinder;
- WriteCmd[3] = Head;
- WriteCmd[4] = sector;
-
- fdcSendCmd(WriteCmd, sizeof(WriteCmd));
- return fdcResultPhase(FALSE, 7);
- }
-
- /* Format the current track */
- unsigned fdcFormatTrack(void* buffer)
- {
- /* Test for buffer fitting within DMA boundries */
- if (!dmaPageTest(buffer, SectorsPerTrack * 4))
- return -5;
-
- /* Setup DMA Controller */
- if (!dmaSetup(DmaCmdWrite, SectorsPerTrack * 4, buffer))
- return -5;
-
- FormatTrackCmd[1] = (Head << 2) | Drive;
- FormatTrackCmd[2] = Head;
-
- fdcSendCmd(FormatTrackCmd, sizeof(FormatTrackCmd));
- return fdcResultPhase(FALSE, 7);
- }
-
- int fdcSendCmd(byte* cmd, unsigned cmdlen)
- {
- int rqmTimeout, i;
-
- /* Send the command to the FDC */
- for (i = 0 ; i < cmdlen ; ++i)
- {
- fdcDelay();
-
- /* Wait for the RQM flag */
- rqmTimeout = 0;
- while ((fdcGetStatus() & FdcMsrRequest) !=
- FdcMsrRequest)
- {
- if (++rqmTimeout > 1000)
- return -1;
- }
-
- /* Check that the FDC is ready for input */
- if ((fdcGetStatus() & FdcMsrDirection) != 0)
- return -2;
-
- fdcWriteCmd(*cmd);
- ++cmd;
- }
-
- return 0;
- }
-
- /* Wait for an interrupt */
- void fdcWaitForInt(void)
- {
- while (!Interrupt)
- ;
-
- Interrupt = FALSE;
- }
-
- /* Wait for result data */
- int fdcResultPhase(int immediate, int nres)
- {
- /* wait for interrupt if not immediate result phase */
- if (!immediate)
- fdcWaitForInt();
-
- /* Turn off the interrupt flag */
- Interrupt = FALSE;
-
- /* Input results from the FDC */
- if (immediate || (fdcGetStatus() & FdcMsrFdcBusy) != 0)
- {
- /* operation is completed, get FDC result bytes */
- int i, rqmTimeout;
-
- for (i = 0 ; i < nres ; ++i)
- {
- fdcDelay();
-
- /* Wait for the RQM flag */
- rqmTimeout = 0;
- while ((fdcGetStatus() & FdcMsrRequest) !=
- FdcMsrRequest)
- {
- if (++rqmTimeout > 1000)
- return -1;
- }
-
- /* Check that the FDC is ready for input */
- if ((fdcGetStatus() & FdcMsrDirection) == 0)
- return -2;
-
- Results[i] = fdcGetResult();
- }
-
- /* Make sure that FDC has completed sending data */
- if ((fdcGetStatus() & FdcMsrFdcBusy) != 0)
- return -3;
- }
- else
- /* Catch all hiddin interrupts */
- while (fdcSenseInterrupt() != 0);
-
- return 0;
- }
-
- /* Test to see if buffer fits within a single DMA page,
- * return TRUE is ok
- */
- int dmaPageTest(byte far* buf, unsigned length)
- {
- unsigned page1, page2;
-
- page1 = (unsigned)(ptrToLong(buf) >> 16) & 0x000f;
- page2 = (unsigned)(ptrToLong(buf + length) >> 16)
- & 0x000f;
-
- return (page1 == page2);
- }
-
- /* Setup the DMA controller */
- int dmaSetup(int fcn, unsigned nbytes, void far* addr)
- {
- unsigned long ptr = ptrToLong(addr);
-
- disable();
-
- /* Set DMA mode and reset the first/last flip-flop */
- outportb(DmaModePort, fcn);
- fdcDelay();
- outportb(DmaFFPort, 0);
- fdcDelay();
-
- /* Set DMA Address */
- outportb(DmaAddrPort, ptr & 0x000000ffL);
- fdcDelay();
- outportb(DmaAddrPort, (ptr & 0x0000ff00L) >> 8);
- fdcDelay();
- outportb(DmaPagePort, (ptr & 0x000f0000L) >> 12);
- fdcDelay();
-
- /* Set transfer word count */
- --nbytes;
- outportb(DmaWcPort, nbytes & 0x00ff);
- fdcDelay();
- outportb(DmaWcPort, (nbytes & 0xff00) >> 8);
- fdcDelay();
-
- enable();
-
- /* Start the DMA channel */
- outportb(DmaMaskPort, DmaFdcChannel);
-
- return 0;
- }
-